// PanelDrag.cpp

#include "StdAfx.h"

#include "Panel.h"

#ifdef UNDER_CE
#include <winuserm.h>
#endif

#include "Common/FileSystem/ArchiveName.h"
#include "Common/FileSystem/FileFind.h"

#include "Common/String/StringConvert.h"

#include "Common/System/Memory.h"


#include "MessagesDialog.h"

#include "App.h"
#include "EnumFormatEtc.h"
#include "FileManager/Windows.h"

using namespace Common;
using namespace System;

static TCHAR* kTempDirPrefix = _T("7zE");
static LPCTSTR kSvenZipSetFolderFormat = TEXT("7-Zip::SetTargetFolder");

using namespace Common;

////////////////////////////////////////////////////////

class ATL_NO_VTABLE CDataObject:
	public IDataObject,
	public CComObjectRootEx<CComMultiThreadModel>
{
private:

	FORMATETC m_Etc;
	UINT m_SetFolderFormat;

	_ThreadModel::AutoCriticalSection m_cs;

	void Lock()
	{
		m_cs.Lock();
	}
	void Unlock()
	{
		m_cs.Unlock();
	}


public:
	BEGIN_COM_MAP(CDataObject)
	COM_INTERFACE_ENTRY(IDataObject)
	END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct();

	void FinalRelease()
	{
	}

	STDMETHODIMP GetData(LPFORMATETC pformatetcIn, LPSTGMEDIUM medium);
	STDMETHODIMP GetDataHere(LPFORMATETC pformatetc, LPSTGMEDIUM medium);
	STDMETHODIMP QueryGetData(LPFORMATETC pformatetc );

	STDMETHODIMP GetCanonicalFormatEtc ( LPFORMATETC /* pformatetc */, LPFORMATETC pformatetcOut)
	{
		pformatetcOut->ptd = NULL;
		return ResultFromScode(E_NOTIMPL);
	}

	STDMETHODIMP SetData(LPFORMATETC etc, STGMEDIUM* medium, BOOL release);
	STDMETHODIMP EnumFormatEtc(DWORD drection, LPENUMFORMATETC* enumFormatEtc);

	STDMETHODIMP DAdvise(FORMATETC* /* etc */, DWORD /* advf */, LPADVISESINK /* pAdvSink */, DWORD* /* pdwConnection */)
	{
		return OLE_E_ADVISENOTSUPPORTED;
	}
	STDMETHODIMP DUnadvise(DWORD /* dwConnection */)
	{
		return OLE_E_ADVISENOTSUPPORTED;
	}
	STDMETHODIMP EnumDAdvise( LPENUMSTATDATA* /* ppenumAdvise */)
	{
		return OLE_E_ADVISENOTSUPPORTED;
	}


	Memory::CGlobal hGlobal;
	CString Path;
};

HRESULT CDataObject::FinalConstruct()
{
	m_SetFolderFormat = RegisterClipboardFormat(kSvenZipSetFolderFormat);
	m_Etc.cfFormat = CF_HDROP;
	m_Etc.ptd = NULL;
	m_Etc.dwAspect = DVASPECT_CONTENT;
	m_Etc.lindex = -1;
	m_Etc.tymed = TYMED_HGLOBAL;
	return S_OK;
}

STDMETHODIMP CDataObject::SetData(LPFORMATETC etc, STGMEDIUM* medium, BOOL /* release */)
{
	if (etc->cfFormat == m_SetFolderFormat && etc->tymed == TYMED_HGLOBAL &&
			etc->dwAspect == DVASPECT_CONTENT && medium->tymed == TYMED_HGLOBAL)
	{
		Lock();
		Path.Empty();
		if (medium->hGlobal == 0)
		{
			return S_OK;
		}
		size_t size = GlobalSize(medium->hGlobal) / sizeof(wchar_t);
		const wchar_t* src = (const wchar_t*)GlobalLock(medium->hGlobal);
		if (src != 0)
		{
			for (size_t i = 0; i < size; i++)
			{
				wchar_t c = src[i];
				if (c == 0)
				{
					break;
				}
				Path += c;
			}
			GlobalUnlock(medium->hGlobal);
			Unlock();
			return S_OK;
		}
	}
	return E_NOTIMPL;
}

static HGLOBAL DuplicateGlobalMem(HGLOBAL srcGlobal)
{
	SIZE_T size = GlobalSize(srcGlobal);
	const void* src = GlobalLock(srcGlobal);
	if (src == 0)
	{
		return 0;
	}
	HGLOBAL destGlobal = GlobalAlloc(GHND | GMEM_SHARE, size);
	if (destGlobal != 0)
	{
		void* dest = GlobalLock(destGlobal);
		if (dest == 0)
		{
			GlobalFree(destGlobal);
			destGlobal = 0;
		}
		else
		{
			memcpy(dest, src, size);
			GlobalUnlock(destGlobal);
		}
	}
	GlobalUnlock(srcGlobal);
	return destGlobal;
}

STDMETHODIMP CDataObject::GetData(LPFORMATETC etc, LPSTGMEDIUM medium)
{
	HRESULT hRes(QueryGetData(etc));
	if(S_OK != hRes)
	{
		return hRes;
	}
	Lock();
	medium->tymed = m_Etc.tymed;
	medium->pUnkForRelease = 0;
	medium->hGlobal = DuplicateGlobalMem(hGlobal);
	Unlock();
	if (medium->hGlobal == 0)
	{
		return E_OUTOFMEMORY;
	}
	return S_OK;
}

STDMETHODIMP CDataObject::GetDataHere(LPFORMATETC /* etc */, LPSTGMEDIUM /* medium */)
{
	// Seems Windows doesn't call it, so we will not implement it.
	return E_UNEXPECTED;
}


STDMETHODIMP CDataObject::QueryGetData(LPFORMATETC etc)
{
	Lock();
	if ((m_Etc.tymed & etc->tymed) &&
			m_Etc.cfFormat == etc->cfFormat &&
			m_Etc.dwAspect == etc->dwAspect)
	{
		return S_OK;
	}
	Unlock();
	return DV_E_FORMATETC;
}

STDMETHODIMP CDataObject::EnumFormatEtc(DWORD direction, LPENUMFORMATETC FAR* enumFormatEtc)
{
	if (direction != DATADIR_GET)
	{
		return E_NOTIMPL;
	}
	return CreateEnumFormatEtc(1, &m_Etc, enumFormatEtc);
}

////////////////////////////////////////////////////////

class ATL_NO_VTABLE CDropSource:
	public IDropSource,
	public CComObjectRootEx<CComMultiThreadModel>
{
	DWORD m_Effect;
	_ThreadModel::AutoCriticalSection m_cs;

	void Lock()
	{
		m_cs.Lock();
	}
	void Unlock()
	{
		m_cs.Unlock();
	}

public:

	BEGIN_COM_MAP(CDropSource)
	COM_INTERFACE_ENTRY(IDropSource)
	END_COM_MAP()



	DECLARE_PROTECT_FINAL_CONSTRUCT()

	HRESULT FinalConstruct()
	{
		return S_OK;
	}

	void FinalRelease()
	{

	}

	STDMETHOD(QueryContinueDrag)(BOOL escapePressed, DWORD keyState);
	STDMETHOD(GiveFeedback)(DWORD effect);


	bool NeedExtract;
	CPanel* Panel;
	std::vector<UINT32> Indices;
	CString Folder;
	CDataObject* DataObjectSpec;
	CComPtr<IDataObject> DataObject;

	bool NeedPostCopy;
	HRESULT Result;
	CStringVector Messages;

	CDropSource(): NeedPostCopy(false), Panel(0), Result(S_OK), m_Effect(DROPEFFECT_NONE) {}
};

STDMETHODIMP CDropSource::QueryContinueDrag(BOOL escapePressed, DWORD keyState)
{
	if (escapePressed == TRUE)
	{
		return DRAGDROP_S_CANCEL;
	}
	if ((keyState & MK_LBUTTON) == 0)
	{
		Lock();
		DWORD effect = m_Effect;
		bool needExtract = NeedExtract;
		Unlock();

		if (DROPEFFECT_NONE == effect)
		{
			return DRAGDROP_S_CANCEL;
		}
		Result = S_OK;

		if (!DataObjectSpec->Path.IsEmpty())
		{
			needExtract = false;
			Lock();
			NeedPostCopy = true;
			Unlock();
		}
		if (needExtract)
		{
			Result = Panel->m_directory.CopyTo(Indices, Folder,
											   false, // moveMode,
											   false, // showMessages
											   &Messages);
			if (Result != S_OK || !Messages.empty())
			{
				return DRAGDROP_S_CANCEL;
			}
			Panel->RefreshTitleAlways();
		}
		return DRAGDROP_S_DROP;
	}
	return S_OK;
}

STDMETHODIMP CDropSource::GiveFeedback(DWORD effect)
{
	Lock();
	m_Effect = effect;
	Unlock();
	return DRAGDROP_S_USEDEFAULTCURSORS;
}

static bool CopyNamesToHGlobal(Memory::CGlobal &hgDrop, const CStringVector &names)
{
	size_t totalLength = 1;
	size_t i;
	for (i = 0; i < names.size(); i++)
	{
		totalLength += names[i].GetLength() + 1;
	}

	if (!hgDrop.Alloc(GHND | GMEM_SHARE, totalLength * sizeof(WCHAR) + sizeof(DROPFILES)))
	{
		return false;
	}

	Memory::CGlobalLock dropLock(hgDrop);
	DROPFILES* dropFiles = (DROPFILES*)dropLock.GetPointer();
	if (dropFiles == 0)
	{
		return false;
	}
	dropFiles->fNC = FALSE;
	dropFiles->pt.x = 0;
	dropFiles->pt.y = 0;
	dropFiles->pFiles = sizeof(DROPFILES);
	dropFiles->fWide = TRUE;
	WCHAR* p = (WCHAR*)((BYTE*)dropFiles + sizeof(DROPFILES));
	for (i = 0; i < names.size(); i++)
	{
		const CString &s = names[i];
		int fullLength = s.GetLength() + 1;
		wcscpy(p, s);
		p += fullLength;
		totalLength -= fullLength;
	}
	*p = 0;

	return true;
}

void CPanel::OnDrag(LPNMLISTVIEW /* nmListView */)
{
	CPanel::CDisableTimerProcessing disableTimerProcessing2(*this);
	if (!m_directory.DoesItSupportOperations())
	{
		return;
	}
	std::vector<UINT32> indices;
	GetOperatedItemIndices(indices);
	if (indices.size() == 0)
	{
		return;
	}

	CString dirPrefix;
	Common::FileSystem::CTempDirectory tempDirectory;

	bool isFSFolder = m_directory.IsFSFolder();
	if (isFSFolder)
	{
		dirPrefix = m_directory.GetCurrentDirectory();
	}
	else
	{
		tempDirectory.Create(kTempDirPrefix);
		dirPrefix = tempDirectory.GetPath();
		FileSystem::FileName::NormalizeDirPathPrefix(dirPrefix);
	}

	CDataObject* dataObjectSpec = new CComObject<CDataObject>;
	dataObjectSpec->FinalConstruct();
	CComPtr<IDataObject> dataObject = dataObjectSpec;

	{
		CStringVector names;
		for (size_t i = 0; i < indices.size(); i++)
		{
			UINT32 index = indices[i];
			CString s;
			if (isFSFolder)
			{
				s = m_directory.GetItemRelPath(index);
			}
			else
			{
				s = m_directory.GetItemName(index);
			}
			names.push_back(dirPrefix + s);
		}
		if (!CopyNamesToHGlobal(dataObjectSpec->hGlobal, names))
		{
			return;
		}
	}

	CDropSource* dropSourceSpec = new CComObject<CDropSource>;
	dropSourceSpec->FinalConstruct();
	CComPtr<IDropSource> dropSource = dropSourceSpec;
	dropSourceSpec->NeedExtract = !isFSFolder;
	dropSourceSpec->Panel = this;
	dropSourceSpec->Indices.assign(indices.begin(), indices.end());
	dropSourceSpec->Folder = dirPrefix;
	dropSourceSpec->DataObjectSpec = dataObjectSpec;
	dropSourceSpec->DataObject = dataObjectSpec;

	bool moveIsAllowed = isFSFolder;

	DWORD effectsOK = DROPEFFECT_COPY;
	if (moveIsAllowed)
	{
		effectsOK |= DROPEFFECT_MOVE;
	}
	DWORD effect;
	m_pApp->DragBegin();
	HRESULT res = DoDragDrop(dataObject, dropSource, effectsOK, &effect);
	m_pApp->DragEnd();
	bool canceled = (res == DRAGDROP_S_CANCEL);
	if (res == DRAGDROP_S_DROP)
	{
		res = dropSourceSpec->Result;
		if (dropSourceSpec->NeedPostCopy)
			if (!dataObjectSpec->Path.IsEmpty())
			{
				FileSystem::FileName::NormalizeDirPathPrefix(dataObjectSpec->Path);
				res = m_directory.CopyTo(indices, dataObjectSpec->Path,
										 (effect == DROPEFFECT_MOVE), false,  &dropSourceSpec->Messages);
			}

	}
	else
	{
		if (res != DRAGDROP_S_CANCEL && res != S_OK)
		{
			FileManager::CWindows::MessageBoxError(*this, res);
		}
		res = dropSourceSpec->Result;
	}

	if (!dropSourceSpec->Messages.empty())
	{
		CMessagesDialog messagesDialog;
		messagesDialog.Messages = &dropSourceSpec->Messages;
		messagesDialog.DoModal();
	}
	if (res != S_OK && res != E_ABORT)
	{
		FileManager::CWindows::MessageBoxError(*this, res);
	}
	if (res == S_OK && dropSourceSpec->Messages.empty() && !canceled)
	{
		KillSelection();
		RefreshTitleAlways();
	}
}

void CDropTarget::QueryGetData(IDataObject* dataObject)
{
	FORMATETC etc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
	m_DropIsAllowed = (dataObject->QueryGetData(&etc) == S_OK);

}

static void MySetDropHighlighted(HWND hWnd, int index, bool enable)
{
	LVITEM item;
	item.mask = LVIF_STATE;
	item.iItem = index;
	item.iSubItem = 0;
	item.state = enable ? LVIS_DROPHILITED : 0;
	item.stateMask = LVIS_DROPHILITED;
	item.pszText = 0;
	ListView_SetItem(hWnd, &item);
}

void CDropTarget::RemoveSelection()
{
	if (m_SelectionIndex >= 0 && m_Panel != 0)
	{
		MySetDropHighlighted(m_Panel->_listView, m_SelectionIndex, false);
	}
	m_SelectionIndex = -1;
}

#ifdef UNDER_CE
#define ChildWindowFromPointEx(hwndParent, pt, uFlags) ChildWindowFromPoint(hwndParent, pt)
#endif

//check if needed
void CDropTarget::PositionCursor(POINTL ptl)
{
	m_SubFolderIndex = -1;
	POINT pt;
	pt.x = ptl.x;
	pt.y = ptl.y;

	RemoveSelection();
	m_IsAppTarget = true;
	m_Panel = NULL;

	if (!m_DropIsAllowed)
	{
		return;
	}
	{
		POINT pt2 = pt;
		pMainWnd->_window.ScreenToClient(&pt2);
		if (pMainWnd->m_wndPanel.IsWindowEnabled())
			if (ChildWindowFromPointEx(pMainWnd->_window, pt2,
									   CWP_SKIPINVISIBLE | CWP_SKIPDISABLED) == (HWND)pMainWnd->m_wndPanel)
			{
				m_Panel = &pMainWnd->m_wndPanel;
				m_IsAppTarget = false;
			}
		if (m_IsAppTarget)
		{
			m_Panel = &pMainWnd->m_wndPanel;
			return;
		}
	}

	if (!m_Panel->m_directory.IsFsOrPureDrivesFolder())
	{
		return;
	}

	if (WindowFromPoint(pt) != (HWND)m_Panel->_listView)
	{
		return;
	}

	LVHITTESTINFO info;
	m_Panel->_listView.ScreenToClient(&pt);
	info.pt = pt;
	int index = ListView_HitTest(m_Panel->_listView, &info);
	if (index < 0)
	{
		return;
	}
	int realIndex = m_Panel->GetRealItemIndex(index);
	if (realIndex == FileManager::FileSystem::kParentIndex)
	{
		return;
	}
	if (!m_Panel->m_directory.IsItemFolder(realIndex))
	{
		return;
	}
	m_SubFolderIndex = realIndex;
	m_SubFolderName = m_Panel->m_directory.GetItemName(m_SubFolderIndex);
	MySetDropHighlighted(m_Panel->_listView, index, true);
	m_SelectionIndex = index;
}

bool CDropTarget::IsFsFolderPath() const
{
	if (!m_IsAppTarget && m_Panel != 0)
	{
		return (m_Panel->m_directory.IsFSFolder() || (m_Panel->m_directory.IsFSDrivesFolder() && m_SelectionIndex >= 0));
	}
	return false;
}

static void ReadUnicodeStrings(const wchar_t* p, size_t size, CStringVector &names)
{
	names.clear();
	CString name;
	for (; size > 0; size--)
	{
		wchar_t c = *p++;
		if (c == 0)
		{
			if (name.IsEmpty())
			{
				break;
			}
			names.push_back(name);
			name.Empty();
		}
		else
		{
			name += c;
		}
	}
}

static void ReadAnsiStrings(const char* p, size_t size, CStringVector &names)
{
	names.clear();
	CStringA name;
	for (; size > 0; size--)
	{
		char c = *p++;
		if (c == 0)
		{
			if (name.IsEmpty())
			{
				break;
			}
			names.push_back(String::GetUnicodeString(name));
			name.Empty();
		}
		else
		{
			name += c;
		}
	}
}

static void GetNamesFromDataObject(IDataObject* dataObject, CStringVector &names)
{
	names.clear();
	FORMATETC etc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
	STGMEDIUM medium;
	HRESULT res = dataObject->GetData(&etc, &medium);
	if (res != S_OK)
	{
		return;
	}
	if (medium.tymed != TYMED_HGLOBAL)
	{
		return;
	}
	{
		Memory::CGlobal global;
		global.Attach(medium.hGlobal);
		size_t blockSize = GlobalSize(medium.hGlobal);
		Memory::CGlobalLock dropLock(medium.hGlobal);
		const DROPFILES* dropFiles = (DROPFILES*)dropLock.GetPointer();
		if (dropFiles == 0)
		{
			return;
		}
		if (blockSize < dropFiles->pFiles)
		{
			return;
		}
		size_t size = blockSize - dropFiles->pFiles;
		const void* namesData = (const BYTE*)dropFiles + dropFiles->pFiles;
		if (dropFiles->fWide)
		{
			ReadUnicodeStrings((const wchar_t*)namesData, size / sizeof(wchar_t), names);
		}
		else
		{
			ReadAnsiStrings((const char*)namesData, size, names);
		}
	}
}

bool CDropTarget::IsItSameDrive() const
{
	if (m_Panel == 0)
	{
		return false;
	}
	if (!IsFsFolderPath())
	{
		return false;
	}
	CString drive;
	if (m_Panel->m_directory.IsFSFolder())
	{
		drive = m_Panel->m_directory.GetDriveOrNetworkPrefix();
		if (drive.IsEmpty())
		{
			return false;
		}
	}
	else if (m_Panel->m_directory.IsFSDrivesFolder() && m_SelectionIndex >= 0)
	{
		drive = m_SubFolderName + CHAR_PATH_SEPARATOR;
	}
	else
	{
		return false;
	}

	if (m_SourcePaths.size() == 0)
	{
		return false;
	}
	for (size_t i = 0; i < m_SourcePaths.size(); i++)
	{
		const CString &path = m_SourcePaths[i];
		if (drive.CompareNoCase(path.Left(drive.GetLength())) != 0)
		{
			return false;
		}
	}
	return true;

}

DWORD CDropTarget::GetEffect(DWORD keyState, POINTL /* pt */, DWORD allowedEffect)
{
	if (!m_DropIsAllowed) // || !m_PanelDropIsAllowed)
	{
		return DROPEFFECT_NONE;
	}

	if (!IsFsFolderPath() || !m_SetPathIsOK)
	{
		allowedEffect &= ~DROPEFFECT_MOVE;
	}

	DWORD effect = 0;
	if (keyState & MK_CONTROL)
	{
		effect = allowedEffect & DROPEFFECT_COPY;
	}
	else if (keyState & MK_SHIFT)
	{
		effect = allowedEffect & DROPEFFECT_MOVE;
	}
	if (effect == 0)
	{
		if (allowedEffect & DROPEFFECT_COPY)
		{
			effect = DROPEFFECT_COPY;
		}
		if (allowedEffect & DROPEFFECT_MOVE)
		{
			if (IsItSameDrive())
			{
				effect = DROPEFFECT_MOVE;
			}
		}
	}
	if (effect == 0)
	{
		return DROPEFFECT_NONE;
	}
	return effect;
}

CString CDropTarget::GetTargetPath() const
{
	if (!IsFsFolderPath())
	{
		return CString();
	}
	CString path = m_Panel->m_directory.GetCurrentDirectory();
	if (m_Panel->m_directory.IsFSDrivesFolder())
	{
		path.Empty();
	}
	if (m_SubFolderIndex >= 0 && !m_SubFolderName.IsEmpty())
	{
		path += m_SubFolderName;
		path += CHAR_PATH_SEPARATOR;
	}
	return path;
}

bool CDropTarget::SetPath(bool enablePath) const
{
	UINT setFolderFormat = RegisterClipboardFormat(kSvenZipSetFolderFormat);

	FORMATETC etc = { (CLIPFORMAT)setFolderFormat, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
	STGMEDIUM medium;
	medium.tymed = etc.tymed;
	medium.pUnkForRelease = 0;
	CString path;
	if (enablePath)
	{
		path = GetTargetPath();
	}
	size_t size = path.GetLength() + 1;
	medium.hGlobal = GlobalAlloc(GHND | GMEM_SHARE, size * sizeof(wchar_t));
	if (medium.hGlobal == 0)
	{
		return false;
	}
	wchar_t* dest = (wchar_t*)GlobalLock(medium.hGlobal);
	if (dest == 0)
	{
		GlobalUnlock(medium.hGlobal);
		return false;
	}
	wcscpy(dest, path);
	GlobalUnlock(medium.hGlobal);
	bool res = m_DataObject->SetData(&etc, &medium, FALSE) == S_OK;
	GlobalFree(medium.hGlobal);
	return res;
}

bool CDropTarget::SetPath()
{
	m_SetPathIsOK = SetPath(m_DropIsAllowed && IsFsFolderPath());
	return m_SetPathIsOK;
}

STDMETHODIMP CDropTarget::DragEnter(IDataObject* dataObject, DWORD keyState,
									POINTL pt, DWORD* effect)
{
	GetNamesFromDataObject(dataObject, m_SourcePaths);
	QueryGetData(dataObject);
	m_DataObject = dataObject;
	return DragOver(keyState, pt, effect);
}


STDMETHODIMP CDropTarget::DragOver(DWORD keyState, POINTL pt, DWORD* effect)
{
	PositionCursor(pt);
	SetPath();
	*effect = GetEffect(keyState, pt, *effect);
	return S_OK;
}


STDMETHODIMP CDropTarget::DragLeave()
{
	RemoveSelection();
	SetPath(false);
	m_DataObject.Release();
	return S_OK;
}

// We suppose that there was ::DragOver for same POINTL_pt before ::Drop
// So SetPath() is same as in Drop.

STDMETHODIMP CDropTarget::Drop(IDataObject* dataObject, DWORD keyState,
							   POINTL pt, DWORD* effect)
{
	QueryGetData(dataObject);
	PositionCursor(pt);
	m_DataObject = dataObject;
	bool needDrop = true;
	if (m_DropIsAllowed) // && m_PanelDropIsAllowed)
		if (IsFsFolderPath())
		{
			needDrop = !SetPath();
		}
	*effect = GetEffect(keyState, pt, *effect);
	if (m_DropIsAllowed) // && m_PanelDropIsAllowed)
	{
		if (needDrop)
		{
			CString path = GetTargetPath();
			if (m_IsAppTarget && m_Panel)
				if (m_Panel->m_directory.IsFSFolder())
				{
					path = m_Panel->m_directory.GetCurrentDirectory();
				}
			m_Panel->DropObject(dataObject, path);
		}
	}
	RemoveSelection();
	m_DataObject.Release();
	return S_OK;
}

void CPanel::DropObject(IDataObject* dataObject, const CString &folderPath)
{
	CStringVector names;
	GetNamesFromDataObject(dataObject, names);
	CompressDropFiles(names, folderPath);
}


static bool IsFolderInTemp(const CString &path)
{
	CString tempPath;
	if (!Common::FileSystem::GetTempPath(tempPath))
	{
		return false;
	}
	if (tempPath.IsEmpty())
	{
		return false;
	}
	return (tempPath.CompareNoCase(path.Left(tempPath.GetLength())) == 0);
}

static bool AreThereNamesFromTemp(const CStringVector &fileNames)
{
	CString tempPath;
	if (!Common::FileSystem::GetTempPath(tempPath))
	{
		return false;
	}
	if (tempPath.IsEmpty())
	{
		return false;
	}
	for (size_t i = 0; i < fileNames.size(); i++)
		if (tempPath.CompareNoCase(fileNames[i].Left(tempPath.GetLength())) == 0)
		{
			return true;
		}
	return false;
}

void CPanel::CompressDropFiles(const CStringVector &fileNames, const CString &folderPath)
{
	if (fileNames.size() == 0)
	{
		return;
	}
	bool createNewArchive = true;
	if (!m_directory.IsFSFolder())
	{
		createNewArchive = !m_directory.DoesItSupportOperations();
	}

	if (createNewArchive)
	{
		CString folderPath2 = folderPath;
		if (folderPath2.IsEmpty())
		{
			Common::FileSystem::GetOnlyDirPrefix(fileNames.front(), folderPath2);
			if (IsFolderInTemp(folderPath2))
			{
				folderPath2 = ROOT_FS_FOLDER;
			}
		}
		const CString archiveName = FileSystem::CreateArchiveName(fileNames.front(), (fileNames.size() > 1), false);
		Archive::CompressFiles(folderPath2, archiveName, _T(""), fileNames,
							   false, // email
							   true, // showDialog
							   AreThereNamesFromTemp(fileNames) // waitFinish
							  );
	}
	else
	{
		CopyFromAsk(fileNames);
		RefreshTitleAlways();
	}
}
